/*+*********************************************************
Filename: B04_fft-ip_LPF\src\top.v
Description:
  Learning the usage of Gowin FFT IP.
  Try to process the mic data through FFT and IFFT and then output to dac.
  Try to add LPF between FFT and IFFT.

Modification:
2024.04.11 creation by H.Zheng
           copied from B03_fft-ip_app. Set some frequency domain value to zero.

**********************************************************-*/


module top(
	input wire clk,
	input wire rst_n,
  output wire [5:0] led, 

  input wire button,

  output wire mic_ws,
  output wire mic_ck,
  output wire mic_lr,
  input wire mic_data,

  output wire dac_ws,
  output wire dac_bck,
  output wire dac_data,
  output wire dac_pa_en
);

/**
 * clock section
 */
  wire clk1m;
  wire clk6m;
  PLL_6M PLL6m(
    .clkout(clk6m),
    .clkoutd(clk1m),
    .clkin(clk)
  );


/**
 * mic section
 */
  assign mic_lr = 1'b0;

  reg [7:0] mic_counter;

  always @(posedge clk6m or negedge rst_n) begin
    if (!rst_n)
      mic_counter <= 8'd0;
    else
      mic_counter <= mic_counter + 1'd1;
  end

  wire clk_3072kHz_n = mic_counter[0];  //6/2
  wire clk_48kHz_n   = mic_counter[6]; //3/64=6/128

  assign mic_ck = clk_3072kHz_n;
  assign mic_ws = clk_48kHz_n;


  //
  // receive mic data
  //
  reg [63:0] shift_reg;

  always @(posedge mic_ck) begin
    shift_reg <= {shift_reg[62:0], mic_data};	
  end	
      
  reg [63:0] data_reg;
  always  @(negedge mic_ws) begin
    data_reg <= shift_reg;
  end		

  wire[23:0] data_l = data_reg[62:39];	
  wire[23:0] data_r = data_reg[30:7];	    

  //assume that it's signed data    
  wire[22:0] l_amplitude = data_l[23] ? ((~data_l[22:0])+1'b1) : data_l[22:0];
  wire loud_voice = (l_amplitude[22:16]>=7'h07) ? 1'b1 : 1'b0;    

  //trigger signal
  reg [1:0] mic_ws_edge;
  always @(negedge clk) begin
    mic_ws_edge <= {mic_ws_edge[0], mic_ws};	
  end

/**
 * input fifo
 */

  wire [23:0] micdata = { data_l[23], data_l[19:0], 3'b100};  //8 times of original volume

  wire infifo_full_flag;
  wire infifo_rd_clk;
  wire infifo_rd_en;
  wire [23:0] infifo_outdata;

	FIFO_1024x24_Top m_input_fifo(
		.Data(micdata), //input [23:0] Data
		.WrClk(mic_ws), //input WrClk
		.RdClk(infifo_rd_clk), //input RdClk
		.WrEn(1'b1), //input WrEn
		.RdEn(infifo_rd_en), //input RdEn
		.Q(infifo_outdata), //output [23:0] Q
		.Empty(), //output Empty
		.Full(infifo_full_flag) //output Full
	);

/**
 * FFT
 */
  wire mfft_busy_flag;
  wire mfft_input_data_flag;
  wire mfft_start_output_data_flag;
  wire mfft_output_data_flag;
  reg mfft_start;
  wire [23:0] mfft_xk_re_o, mfft_xk_im_o;

  wire [23:0] xn_re_i = infifo_outdata;
  wire mfft_clk = clk1m;
  wire mfft_rst = ~rst_n;

  wire mfft_end_of_output_data_flag;
  wire mfft_start_input_data_flag;

  wire [9:0] mfft_idx;

	FFT_Top_1024p m_fft(
		.idx(mfft_idx), //output [9:0] idx
		.xk_re(mfft_xk_re_o), //output [23:0] xk_re
		.xk_im(mfft_xk_im_o), //output [23:0] xk_im
		.sod(mfft_start_input_data_flag), //output sod
		.ipd(mfft_input_data_flag), //output ipd
		.eod(), //output eod
		.busy(mfft_busy_flag), //output busy
		.soud(mfft_start_output_data_flag), //output soud
		.opd(mfft_output_data_flag), //output opd
		.eoud(mfft_end_of_output_data_flag), //output eoud
		.xn_re(xn_re_i), //input [23:0] xn_re
		.xn_im(24'b0), //input [23:0] xn_im
		.start(mfft_start), //input start
		.clk(mfft_clk), //input clk
		.rst(mfft_rst), //input rst
		.ifft(1'b0) //input ifft
	);

  //trigger input-fifo
  assign infifo_rd_clk = mfft_input_data_flag & mfft_clk;
  assign infifo_rd_en = 1'b1;

//FSM
  localparam S_FFT_IDLE = 1;
  localparam S_FFT_START = 2;
  localparam S_FFT_STARTINPUT = 3;
  localparam S_FFT_ENDOFOUTPUT = 4;

  reg[2:0] fft_state;
  reg[2:0] fft_next_state;

  wire fsm_clk = ~mfft_clk;

  always@(posedge fsm_clk or negedge rst_n) begin
    if (!rst_n)
      fft_state <= S_FFT_IDLE;
    else
      fft_state <= fft_next_state;
  end

  always@(*) begin
    case(fft_state)
      S_FFT_IDLE:
        if(infifo_full_flag == 1'b1)
          fft_next_state <= S_FFT_START;
        else
          fft_next_state <= S_FFT_IDLE;
      S_FFT_START:
        if(mfft_start_input_data_flag == 1'b1)
          fft_next_state <= S_FFT_STARTINPUT;
        else
          fft_next_state <= S_FFT_START;
      S_FFT_STARTINPUT:
        if(mfft_end_of_output_data_flag == 1'b1)
          fft_next_state <= S_FFT_ENDOFOUTPUT;
        else
          fft_next_state <= S_FFT_STARTINPUT;
      S_FFT_ENDOFOUTPUT:
        fft_next_state <= S_FFT_IDLE;
      default:
        fft_next_state <= S_FFT_IDLE;
    endcase
  end

  always@(posedge fsm_clk or negedge rst_n)
  begin
    if(!rst_n)
      mfft_start <= 1'b0;
    else if(fft_state == S_FFT_START)
      mfft_start <= 1'b1;
    else
      mfft_start <= 1'b0;
  end


/**
 * IFFT
 */
  reg [23:0] mifft_xn_re, mifft_xn_im;
  wire [23:0] mifft_xk_re_o, mifft_xk_im_o;
  wire mifft_output_data_flag;
  wire mifft_start;
  wire mifft_busy_flag;

  //since mfft_start_output_data_flag and first mifft_xk_re_o output are at same fft clk, must delay mifft_xk_re_o
  // to match the timing of iFFT

  //add Low Pass Filter
  wire [23:0] mfft_xk_re_lpf, mfft_xk__im_lpf;
  assign mfft_xk_re_lpf = (button == 1'b1) ? mfft_xk_re_o :
                      ((mfft_idx > 100) && (mfft_idx < 924)) ? 24'b0 : mfft_xk_re_o;
  assign mfft_xk__im_lpf = (button == 1'b1) ? mfft_xk_im_o :
                      ((mfft_idx > 100) && (mfft_idx < 924)) ? 24'b0 : mfft_xk_im_o;


  //shift FFT's output data as IFFT's input
  reg [23:0] mifft_xn_re_delay, mifft_xn_im_delay;
  always @(negedge mfft_clk) begin
//    mifft_xn_re_delay <= mfft_xk_re_o;	
//    mifft_xn_im_delay <= mfft_xk_im_o;	
    mifft_xn_re_delay <= mfft_xk_re_lpf;	
    mifft_xn_im_delay <= mfft_xk__im_lpf;	
  end  
  always @(posedge mfft_clk) begin
    mifft_xn_re <= mifft_xn_re_delay;	
    mifft_xn_im <= mifft_xn_im_delay;	
  end  

	FFT_Top_1024p m_ifft(
		.idx(), //output [9:0] idx
		.xk_re(mifft_xk_re_o), //output [23:0] xk_re
		.xk_im(mifft_xk_im_o), //output [23:0] xk_im
		.sod(), //output sod
		.ipd(), //output ipd
		.eod(), //output eod
		.busy(mifft_busy_flag), //output busy
		.soud(), //output soud
		.opd(mifft_output_data_flag), //output opd
		.eoud(), //output eoud
		.xn_re(mifft_xn_re), //input [23:0] xn_re
		.xn_im(mifft_xn_im), //input [23:0] xn_im
		.start(mifft_start), //input start
		.clk(mfft_clk), //input clk
		.rst(mfft_rst), //input rst
		.ifft(1'b1) //input ifft
	);
  //trigger ifft start by mfft_start_output_data_flag
  assign mifft_start = mfft_start_output_data_flag;

/**
 * output fifo
 */

  wire [23:0] outfifo_indata;
  wire outfifo_wr_clk;
  wire outfifo_wr_en;
  wire outfifo_rd_clk;
  wire outfifo_rd_en;
  wire [23:0] outfifo_outdata;


	FIFO_1024x24_Top m_output_fifo(
		.Data(outfifo_indata), //input [23:0] Data
		.WrClk(outfifo_wr_clk), //input WrClk
		.RdClk(outfifo_rd_clk), //input RdClk
		.WrEn(outfifo_wr_en), //input WrEn
		.RdEn(outfifo_rd_en), //input RdEn
		.Q(outfifo_outdata), //output [23:0] Q
		.Empty(), //output Empty
		.Full() //output Full
	);

  assign outfifo_indata = mifft_xk_re_o;
  assign outfifo_wr_clk = ~mfft_clk; //clk in data via negedge
  assign outfifo_wr_en = mifft_output_data_flag;

  //to DAC
  assign outfifo_rd_en = 1'b1;
  assign outfifo_rd_clk = mic_ws; //tx_data_valid;

/**
 * DAC interface
 */

  wire [15:0] dac_data_r, dac_data_l;

  assign dac_bck = mic_counter[1]; //32 bck per ws period

  assign dac_data_l = {outfifo_outdata[23], outfifo_outdata[14:0]};
  assign dac_data_r = {outfifo_outdata[23], outfifo_outdata[14:0]};

  dac_pt8211 pt8211_u1(
    .data_in_clk(~outfifo_rd_clk),
    .data_r(dac_data_r),
    .data_l(dac_data_l),
    .dac_if_bck(dac_bck),
    .dac_if_ws(dac_ws),
    .dac_if_data(dac_data)
  );

  assign dac_pa_en = 1'b1;

/**
 * led
 */

  wire outfifo_outdata_zero = (outfifo_outdata == 24'b0);
  wire[22:0] amplitude2 = outfifo_outdata[23] ? ((~outfifo_outdata[22:0])+1'b1) : outfifo_outdata[22:0];
  wire loud_voice2 = (amplitude2[22:8]>=15'h07) ? 1'b1 : 1'b0;    

  assign led = {~loud_voice2, 3'b111, ~outfifo_outdata_zero , ~loud_voice};

endmodule